<?php

namespace App\Models\Common;

use App\Exceptions\DeviceClientException;
use GuzzleHttp\Client;
use Illuminate\Support\Arr;

class VziCloud
{
    const TIME_OUT_SECONDS = '5';
    const METHOD_POST = 'post';
    const GATEWAY = 'https://open.vzicloud.com';
    const CONTENT_TYPE = 'application/json';

    protected $accessKeyId = '';
    protected $accessKeySecret = '';
    protected $username = 'admin';
    protected $password = 'admin';

    protected $gateway = '';
    protected $uri = '';
    protected $name = '';
    private $httpMethod = self::METHOD_POST;
    private $contentType = self::CONTENT_TYPE;
    private $timeout = self::TIME_OUT_SECONDS;
    protected $urlBody = [];
    protected $queryBody = [];
    protected $addLog = true;
    protected $cancel = false;
    protected $errBox = [];

    public function __construct(array $config = [])
    {
        if ($accessKeyId = Arr::get($config, 'accessKeyId')) {
            $this->accessKeyId = $accessKeyId;
        } else {
            $this->accessKeyId = config('services.vzicloud.accessKeyId');
        }
        if ($accessKeySecret = Arr::get($config, 'accessKeySecret')) {
            $this->accessKeySecret = $accessKeySecret;
        } else {
            $this->accessKeySecret = config('services.vzicloud.accessKeySecret');
        }

        if ($gateway = \Arr::get($config, 'gateway')) {
            $this->gateway = $gateway;
        } else {
            $this->gateway = self::GATEWAY;
        }

        $this->configValidator();
    }

    public function setTimeout(int $sec): self
    {
        $this->timeout = $sec;

        return $this;
    }

    public function addGroup(array $queryPacket)
    {

        $this->name = '创建分组';
        $this->uri = '/openapi/v1/stp/user/groups';

        if (!$name = Arr::get($queryPacket, 'name')) {
            $this->errBox[] = '分组名称为空';
            $this->cancel = true;
        }

        $this->queryBody = [
            'name' => $name,
//            'remark' => ''
        ];

        return $this;
    }

    public function groups(array $queryPacket)
    {

        $this->name = '创建分组';
        $this->uri = '/openapi/v1/stp/user/groups';
        $this->httpMethod = 'get';

        $urlBody = [];
        if ($size = intval(Arr::get($queryPacket, 'size'))) {
            $urlBody['size'] = $size;
        }
        if ($page = intval(Arr::get($queryPacket, 'page'))) {
            $urlBody['page'] = $page;
        }

        if ($urlBody) {
            $this->urlBody = $urlBody;
        }

        return $this;
    }

    public function addDevice(array $queryPacket)
    {

        $this->name = '绑定设备';
        $this->uri = '/openapi/v1/stp/user/devices';

        if (!$sn = Arr::get($queryPacket, 'sn')) {
            $this->errBox[] = '设设备序列号为空';
            $this->cancel = true;
        }

        $this->queryBody = [[
            'sn' => $sn,
            'group_id' => intval(Arr::get($queryPacket, 'group_id')),
            'username' => Arr::get($queryPacket, 'username') ?: $this->username,
            'password' => Arr::get($queryPacket, 'username') ?: $this->username,
            'remark' => Arr::get($queryPacket, 'remark') ?: ''
        ]];

        // {
        //  "successes": [{"sn": "12345678-87654321", "group_id": 0, "username": "admin", "password": "admin", "remark": ""}],
        //  "failures": [{"sn": "12345678-87654322", "group_id": 0, "username": "admin", "password": "admin", "remark": "", "error": { "code": 1003, "message": "该设备已被绑定" }}]
        //}

        return $this;
    }

    public function deviceInfo(array $queryPacket)
    {
        $this->name = '设备详情';
        $this->uri = '/openapi/v1/stp/user/devices/';
        $this->httpMethod = 'get';

        if (!$sn = Arr::get($queryPacket, 'sn')) {
            $this->errBox[] = '设备序列号为空';
            $this->cancel = true;
        }

        $this->uri .= $sn;

        return $this;
    }

    public function devices(array $queryPacket)
    {

        $this->name = '设备列表';
        $this->uri = '/openapi/v1/stp/user/devices';
        $this->httpMethod = 'get';

        $urlBody = [];
        if ($size = intval(Arr::get($queryPacket, 'size'))) {
            $urlBody['size'] = $size;
        }
        if ($page = intval(Arr::get($queryPacket, 'page'))) {
            $urlBody['page'] = $page;
        }

        if ($urlBody) {
            $this->urlBody = $urlBody;
        }

        return $this;
    }

    public function delDevice(array $queryPacket)
    {
        $this->name = '解绑设备';
        $this->uri = '/openapi/v1/stp/user/devices';
        $this->httpMethod = 'delete';

        if (!$sn = Arr::get($queryPacket, 'sn')) {
            $this->errBox[] = '设备序列号为空';
            $this->cancel = true;
        }

        $this->queryBody = [[
            'sn' => $sn,
        ]];

        // {
        //  "successes": [{"sn": "12345678-87654321"}],
        //  "failures": [{"sn": "12345678-87654322", "error": { "code": 1003, "message": "无权访问此设备" }} ]
        //}

        return $this;
    }

    public function deviceWebUrl(array $queryPacket)
    {
        $this->name = '远程访问设备网页';
        $this->uri = '/openapi/v1/stp/user/devices/pdns/web';
        $this->httpMethod = 'get';

        if (!$sn = Arr::get($queryPacket, 'sn')) {
            $this->errBox[] = '设备序列号为空';
            $this->cancel = true;
        }

        $this->urlBody = [
            'sn' => $sn,
            'type' => 'json'
        ];

        // {
        //  "proto": "http",
        //  "host":"192.168.6.111",
        //  "userinfo":"U0TlxxxxxxxxxxxxxxxxxvjA==",
        //  "port":17507,
        //  "url": "http://192.168.6.111:17507/12345678-12345678/?userdata=pdns&userinfo=U0TlxxxxxxxxxxxxxxxxxvjA=="
        //}

        return $this;
    }

    public function deviceVideoUrl(array $queryPacket)
    {
        $this->name = '获取设备实时直播地址';
        $this->uri = '/openapi/v1/stp/user/devices/vurl';
        $this->httpMethod = 'get';

        if (!$sn = Arr::get($queryPacket, 'sn')) {
            $this->errBox[] = '设备序列号为空';
            $this->cancel = true;
        }

        $this->urlBody = [
            'sn' => $sn,
            'type' => 'auto', // rtsp、flv
//            'channel' => '',
//            'auth' => base64_encode('admin:admin'),
        ];

        return $this;
    }

    protected function configValidator()
    {
        if (!$this->accessKeyId) {
            throw new DeviceClientException('accessKeyId为空');
        }

        if (!$this->accessKeySecret) {
            throw new DeviceClientException('accessKeySecret为空');
        }
    }

    public function fire()
    {
        if ($this->cancel) {
            if (is_array($errBox = $this->errBox)) {
                $errMsg = implode(',', $errBox);
            } else {
                $errMsg = '未知错误';
            }
            $responseArr = [
                'message' => $errMsg
            ];

            $this->cleanup();

            return $responseArr;
        }

        $timestamp = time() + 10 * 60; // 过期时间
        $sign = self::generateSign($this->uri, $this->queryBody, $this->urlBody, $timestamp, $this->httpMethod, $this->contentType, $this->accessKeySecret);
//        dd($sign);

        $uri = $this->uri . '?expires='.$timestamp.'&accesskey_id='.$this->accessKeyId.'&signature='.$sign;

        if ($this->urlBody) {
            foreach ($this->urlBody as $key => $value) {
                $uri .= '&' . $key . '=' . $value . '&';
            }
            $uri = rtrim($uri, '&');
        }
//        dd($uri);

        $httpClient = new Client(['base_uri' => $this->gateway, 'timeout' => $this->timeout, 'verify' => false]);
        $httpMethod = $this->httpMethod;

//        $queryBody = ['headers' => ['Content-Type' => 'application/json;charset=utf-8', 'Accept' => 'application/json']];
        $queryBody = [];
        if ($this->queryBody) {
            $queryBody = [
                'headers' => ['Content-Type' => $this->contentType],
                'json' => $this->queryBody
            ];
        }

        try {
            $rawResponse = $httpClient->request($httpMethod, $uri, $queryBody);
            $responseStr = optional(optional($rawResponse)->getBody())->getContents() ?: '';
            $responseArr = @json_decode($responseStr, true) ?: [];
        } catch (\GuzzleHttp\Exception\ClientException $exception) {
            // 获取响应对象
            $response = $exception->getResponse();
            // 获取状态码
            $statusCode = $response->getStatusCode();
            // 获取响应内容
            $errorResponse = $response->getBody()->getContents();

            $responseArr = @json_decode($errorResponse, true) ?: [];

//            $responseArr = [
//                'message' => $exception->getMessage()
//            ];
        } finally {
            if ($this->addLog) {

                \Log::channel('vzicloud')->info($this->name, [
                    'uri' => $this->uri,
                    'url' => $this->urlBody,
                    'query' => $this->queryBody,
                    'response' => $responseArr
                ]);
            }

            self::cleanup();

            return $responseArr;
        }
    }

    protected function generateSign(string $uri, array $queryBody, array $urlBody, int $timestamp, string $httpMethod, string $contentType, string $secret)
    {
        $signStr = strtoupper($httpMethod) . "\n";

        $md5Binary = '';
        $resource = $uri;
        if ($queryBody) {
//            dd($queryBody);
            $md5Binary = base64_encode(md5(json_encode($queryBody), true));

//            $filteredArray = array_filter($queryBody, function($value) {
//                return !empty($value) || $value === '0' || $value === 0;
//            });
//            dd($filteredArray);

        } else {
            $contentType = '';
        }

        if ($urlBody) {
            ksort($urlBody);
            $resource .= '?';
            foreach ($urlBody as $key => $value) {
                $resource .= $key . '=' . $value . '&';
            }
            $resource = rtrim($resource, '&');
        }

        $signStr .= $md5Binary . "\n";
        $signStr .= $contentType . "\n";
        $signStr .= $timestamp . "\n" . $resource;

//        dd($signStr);

        return urlencode(base64_encode(hash_hmac('sha1', $signStr, $secret, true)));
    }

    protected function cleanup()
    {
        $this->urlBody = [];
        $this->queryBody = [];
        $this->uri = '';
        $this->name = '';
        $this->httpMethod = self::METHOD_POST;
    }
}
